package com.atom.test.opengl.gesture

import android.view.MotionEvent
import com.atom.module.logger.Logger
import java.util.*

open class GestureRecognizer {
    var enabled = true

    var centroidX = 0f

    var centroidY = 0f

    var startX = 0f

    var startY = 0f

    var translationX = 0f
    fun translationX(value: Float) {
        this.translationX = value
        this.startX = this.centroidX
        this.centroidShiftX = 0f
    }

    var translationY = 0f
    fun translationY(value: Float) {
        this.translationY = value
        this.startY = this.centroidY
        this.centroidShiftY = 0f
    }

    var centroidShiftX = 0f // 重心指向第一个手指位置 X

    var centroidShiftY = 0f // 重心指向第一个手指位置 Y

    protected var centroidArray = FloatArray(2)

    protected var listenerList = ArrayList<GestureListener>()

    var lowPassWeight = 0.4f

    @GestureListener.GestureState
    var state: Int = GestureListener.POSSIBLE

    private var stateSequence: Long = 0

    constructor(listener: GestureListener) {
        listenerList.add(listener)
    }

    constructor()

    fun addListener(listener: GestureListener) = this.listenerList.add(listener)
    fun removeListener(listener: GestureListener) = this.listenerList.remove(listener)
    fun getListeners(): ArrayList<GestureListener> = this.listenerList
    protected open fun notifyListeners(event: MotionEvent) {
        for (listener in listenerList) {
            listener.gestureStateChanged(event, this)
        }
    }

    /**
     * 重置
     */
    protected open fun reset() {
        this.state = GestureListener.POSSIBLE
        this.stateSequence = 0
        this.centroidX = 0f
        this.centroidY = 0f
        this.startX = 0f
        this.startY = 0f
        this.translationX = 0f
        this.translationY = 0f
        this.centroidShiftX = 0f
        this.centroidShiftY = 0f
    }

    /**
     * 状态变化
     */
    protected fun transitionToState(
        event: MotionEvent,
        @GestureListener.GestureState newState: Int
    ) {
        when (newState) {
            GestureListener.POSSIBLE, GestureListener.FAILED -> {
                state = newState
            }
            GestureListener.RECOGNIZED, GestureListener.BEGAN -> {
                state = newState
                stateSequence++
                this.prepareToRecognize(event)
                notifyListeners(event)
            }
            GestureListener.CHANGED, GestureListener.CANCELLED, GestureListener.ENDED -> {
                state = newState
                stateSequence++
                notifyListeners(event)
            }
        }
    }

    protected open fun prepareToRecognize(event: MotionEvent) {}

    fun onTouchEvent(event: MotionEvent): Boolean {
        if (!enabled) return false

        val currentStateSequence = this.stateSequence

        try {
            val action = event.actionMasked
            when (action) {
                MotionEvent.ACTION_DOWN -> this.handleActionDown(event)
                MotionEvent.ACTION_POINTER_DOWN -> this.handleActionPointerDown(event)
                MotionEvent.ACTION_MOVE -> this.handleActionMove(event)
                MotionEvent.ACTION_CANCEL -> this.handleActionCancel(event)
                MotionEvent.ACTION_POINTER_UP -> this.handleActionPointerUp(event)
                MotionEvent.ACTION_UP -> this.handleActionUp(event)
                else -> {
                    Logger.e("Unrecognized event action $action")
                }
            }
        } catch (e: Exception) {
            Logger.e("Exception handling event %s", e.localizedMessage)
        }

        return currentStateSequence != stateSequence // stateSequence changes if the event was recognized
    }

    /**
     * 初次点击按下
     */
    protected open fun handleActionDown(event: MotionEvent) {
        val index = event.actionIndex
        centroidX = event.getX(index)
        centroidY = event.getY(index)

        startX = centroidX
        startY = centroidY

        translationX = 0f
        translationY = 0f

        centroidShiftX = 0f
        centroidShiftY = 0f
        actionDown(event)
    }

    /**
     * 其次手指按下
     */
    protected open fun handleActionPointerDown(event: MotionEvent) {
        centroidChanged(event)
        actionDown(event)
    }

    /**
     * 手指进行移动
     */
    protected open fun handleActionMove(event: MotionEvent) {
        eventCentroid(event, centroidArray)


        val dx = centroidArray[0] - startX + centroidShiftX
        val dy = centroidArray[1] - startY + centroidShiftY

        centroidX = centroidArray[0]
        centroidY = centroidArray[1]

        translationX = lowPassFilter(translationX, dx)
        translationY = lowPassFilter(translationY, dy)

        actionMove(event)
    }

    /**
     * 手指取消
     */
    protected open fun handleActionCancel(event: MotionEvent) {
        actionCancel(event)
        val state: Int = this.state
        if (state == GestureListener.POSSIBLE) {
            transitionToState(event, GestureListener.FAILED)
        } else if (state == GestureListener.BEGAN || state == GestureListener.CHANGED) {
            transitionToState(event, GestureListener.CANCELLED)
        }
        reset()
    }

    /**
     * 手指抬起
     */
    protected open fun handleActionPointerUp(event: MotionEvent) {
        centroidChanged(event)
        actionUp(event)
    }

    /**
     * 最后一个抬起
     */
    protected open fun handleActionUp(event: MotionEvent) {
        actionUp(event)
        val state = this.state
        if (state == GestureListener.POSSIBLE) {
            transitionToState(event, GestureListener.FAILED)
        } else if (state == GestureListener.BEGAN || state == GestureListener.CHANGED) {
            transitionToState(event, GestureListener.ENDED)
        }
        reset()
    }

    /**
     * 中心位置改变
     */
    protected open fun centroidChanged(event: MotionEvent) {
        centroidShiftX += centroidX //第一个手指的xy 保存到中心点
        centroidShiftY += centroidY
        eventCentroid(event, centroidArray)
        centroidX = centroidArray[0]
        centroidY = centroidArray[1]
        centroidShiftX -= centroidArray[0]
        centroidShiftY -= centroidArray[1]
    }

    /**
     * 计算多指触控重心位置
     */
    protected open fun eventCentroid(event: MotionEvent, result: FloatArray) {
        val index = event.actionIndex //获取手指索引
        val action = event.actionMasked //获取对应状态
        var x = 0f
        var y = 0f
        var count = 0f

        for (idx in 0 until event.pointerCount) {
            if (idx == index && action == MotionEvent.ACTION_POINTER_UP) {
                continue  // suppress coordinates from pointers that are no longer down
            }
            x += event.getX(idx)
            y += event.getY(idx)
            count++
        }
        result[0] = x / count
        result[1] = y / count
    }

    /**
     * 进行权重判断 降低林敏度
     */
    protected fun lowPassFilter(value: Float, newValue: Float): Float {
        val w = lowPassWeight
        return value * (1 - w) + newValue * w
    }

    fun normalizeAngle180(degrees: Double): Double {
        val angle = degrees % 360
        return if (angle > 180) angle - 360 else if (angle < -180) 360 + angle else angle
    }

    protected open fun actionDown(event: MotionEvent) {}

    protected open fun actionMove(event: MotionEvent) {}

    protected open fun actionCancel(event: MotionEvent) {}

    protected open fun actionUp(event: MotionEvent) {}
}